/*
 * Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.huaweicloud;

import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.internal.util.StringUtil;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.spi.utils.PortRange;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.Arrays;

import static com.hazelcast.internal.util.StringUtil.isNullOrEmptyAfterTrim;

/**
 * Ecs Discovery Strategy configuration that corresponds to the properties
 * passed in the Hazelcast configuration and
 * listed in {@link HwProperties}.
 **/
public final class HwConfig {
    private static final ILogger LOGGER = Logger.getLogger(HwConfig.class);
    private final String region;
    private final List<Tag> tags = new ArrayList<>();
    private final String accessKey;
    private final String secretKey;
    private final String iamDomain;
    private final String iamUser;
    private final String iamPassword;
    private final String projectId;
    private final String instanceId;
    private final PortRange hzPort;

    private final Boolean instanceMetadataAvailable;

    @SuppressWarnings("checkstyle:parameternumber")
    // Constructor has a lot of parameters, but it's private.
    private HwConfig(String region, String accessKey, String secretKey, String tagKey, String tagValue,
                     String instanceId, String iamDomain, String iamUser, String iamPassword, String projectId, PortRange hzPort,
                     Boolean instanceMetadataAvailable) {
        this.region = region;
        createTags(tagKey, tagValue);
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.instanceId = instanceId;
        this.iamDomain = iamDomain;
        this.iamUser = iamUser;
        this.iamPassword = iamPassword;
        this.projectId = projectId;
        this.hzPort = hzPort;
        this.instanceMetadataAvailable = instanceMetadataAvailable;
        validateConfig();
    }

    public Boolean isInstanceMetadataAvailable() {
        return instanceMetadataAvailable;
    }

    private void createTags(String tagKeys, String tagValues) {
        Iterator<String> keys = splitValue(tagKeys).iterator();
        Iterator<String> values = splitValue(tagValues).iterator();

        while (keys.hasNext() || values.hasNext()) {
            if (keys.hasNext() && values.hasNext()) {
                tags.add(new Tag(keys.next(), values.next()));
            } else if (keys.hasNext()) {
                tags.add(new Tag(keys.next(), null));
            } else {
                tags.add(new Tag(null, values.next()));
            }
        }
    }

    private static List<String> splitValue(String value) {
        return isNullOrEmptyAfterTrim(value) ? Collections.emptyList() : Arrays.asList(value.split(","));
    }

    // Validate configuration property values
    private void validateConfig() {
        if ((isNullOrEmptyAfterTrim(accessKey) && !isNullOrEmptyAfterTrim(secretKey))
                || (!isNullOrEmptyAfterTrim(accessKey) && isNullOrEmptyAfterTrim(secretKey))) {
            throw new InvalidConfigurationException(
                    "You have to either define both ('access-key', 'secret-key') or none of them");
        }

        if (!instanceMetadataAvailable) {
            LOGGER.info("instance-metadata-available is set to false, validating other properties...");
            if (!StringUtil.isAllNullOrEmptyAfterTrim(projectId, instanceId, region)) {
                throw new InvalidConfigurationException(
                        "Invalid Ecs Discovery config: " + "useInstanceMetadata property is configured as `false`. "
                                + "Please configure all of projectId, instanceId, region");
            }
        } else {
            if (StringUtil.isAnyNullOrEmptyAfterTrim(projectId, instanceId, region)) {
                throw new InvalidConfigurationException(
                        "Invalid Ecs Discovery config: " + "useInstanceMetadata property is configured as `true`. "
                                + "Please DO NOT configure any of projectId, instanceId, region");
            }
        }
    }

    static Builder builder() {
        return new Builder();
    }

    String getAccessKey() {
        return accessKey;
    }

    String getSecretKey() {
        return secretKey;
    }

    String getRegion() {
        return region;
    }

    String getInstanceId() {
        return instanceId;
    }

    String getIamDomain() {
        return iamDomain;
    }

    String getIamUser() {
        return iamUser;
    }

    String getIamPassword() {
        return iamPassword;
    }

    String getProjectId() {
        return projectId;
    }

    PortRange getPort() {
        return hzPort;
    }

    List<Tag> getTags() {
        return tags;
    }

    static class Builder {
        private String accessKey;
        private String secretKey;
        private String instanceId;
        private String region;
        private String iamDomain;
        private String iamUser;
        private String iamPassword;
        private String projectId;
        private String tagKey;
        private String tagValue;
        private PortRange hzPort;
        private Boolean instanceMetadataAvailable;

        Builder setAccessKey(String accessKey) {
            this.accessKey = accessKey;
            return this;
        }

        Builder setSecretKey(String secretKey) {
            this.secretKey = secretKey;
            return this;
        }

        Builder setInstanceId(String instanceId) {
            this.instanceId = instanceId;
            return this;
        }

        Builder setRegion(String region) {
            this.region = region;
            return this;
        }

        Builder setIamDomain(String iamDomain) {
            this.iamDomain = iamDomain;
            return this;
        }

        Builder setIamUser(String iamUser) {
            this.iamUser = iamUser;
            return this;
        }

        Builder setIamPassword(String iamPassword) {
            this.iamPassword = iamPassword;
            return this;
        }

        Builder setProjectId(String projectId) {
            this.projectId = projectId;
            return this;
        }

        Builder setPort(PortRange hzPort) {
            this.hzPort = hzPort;
            return this;
        }

        Builder setTagKey(String tagKey) {
            this.tagKey = tagKey;
            return this;
        }

        Builder setTagValue(String tagValue) {
            this.tagValue = tagValue;
            return this;
        }

        Builder setInstanceMetadataAvailable(Boolean instanceMetadataAvailable) {
            this.instanceMetadataAvailable = instanceMetadataAvailable;
            return this;
        }

        HwConfig build() {
            return new HwConfig(region, accessKey, secretKey, tagKey, tagValue, instanceId, iamDomain, iamUser,
                    iamPassword, projectId, hzPort, instanceMetadataAvailable);
        }
    }
}
